#include "ortho.h"

// optimisations de dlDistance
int d[50][50];

Ortho::Ortho (QObject* parent) : QObject (parent)
{
	loadDics ();
	setCurrentLanguage ("fr");
}
//---------------------------------
void Ortho::loadDics (QString lang)
{
	m_dics.clear ();

	QString dicPath;
	foreach (QString test, Config::dicsPossiblePaths ())
	{
		if (QDir(test).exists ())
		{
			dicPath = test;
			break;
		}
	}

	if (dicPath.isEmpty ())
	{
		qDebug () << tr ("Aucun dictionnaire trouvé !! la cata !");
		return;
	}

	QDir dir (dicPath);

	QString filter (lang.isEmpty () ? "*.dic" : lang + ".dic");

	int wordCount = 0;

	foreach (QString fileName, dir.entryList (QStringList() << filter))
	{
		QFileInfo info (fileName);

		QString lang = info.baseName ();

		if (! m_dics.contains (lang))
		{
			QFile file (dicPath + fileName);
			if (!file.open (QIODevice::ReadOnly | QIODevice::Text))
			{
				qDebug () << QString ("Impossible d'ouvrir le fichier %1").arg (fileName);
			}
			else
			{
				QTextStream in (&file);
				in.setCodec ("UTF-8");

				QString buffer = in.readAll ();
				QStringList content = buffer.split ('\n', QString::SkipEmptyParts);

				m_dics.insert (lang, content);
				wordCount += content.size ();
			}
		}
	}
	foreach (QString lang, m_dics.keys ())
		qDebug () << tr ("Dictionnaire %1 installé").arg (lang);

	setCurrentLanguage ("fr");

	qDebug () << QString ("%1 mots enregistrés").arg (wordCount);
}


QStringList Ortho::languages ()
{
	return QStringList () << m_dics.keys ();
}
const QString& Ortho::currentLanguage ()
{
	return m_currentLanguage;
}
void Ortho::setCurrentLanguage (QString lang)
{
	if (languages ().contains (lang))
	{
		m_currentLanguage = lang;
	}
	else
	{
		m_currentLanguage = "fr";
	}
}

//------------------------------------------------
QStringList Ortho::allWords ()
{
	const QStringList& result = m_dics.value (currentLanguage ());

	return result;
}
//-----------------------------------------------------
QStringList Ortho::suggests (QString target)
{
	// on suppose que le mot n'est PAS dans les dicos

	if (target.length () <= 2) return QStringList ();

	const QStringList& all = allWords ();

	const int targetLen = target.length ();
	const int distMax = targetLen + 1 / 2;	// distance "raisonnable" !

	QHash<int, QString> hash;

	for (QStringList::const_iterator iter = all.constBegin () ; iter != all.constEnd () ; iter++)
	{
		const QString& word (*iter);

		if (qAbs (targetLen - word.length ()) <= 2) // si l'écart de longueur pas trop grand
		{
			int dist = dlDistance (target, word, distMax);
			if (dist != -1)
				hash.insertMulti (dist, word);
		}
	}

	int min = hash.keys ().first ();

	return hash.values (min);
}
//--------------------------------------------------------------------------------
inline int Ortho::dlDistance (const QString &str1, const QString &str2, const int limit)
{
	// cf. http://fr.wikipedia.org/wiki/Distance_de_Damerau-Levenshtein

	const int lenStr1 = str1.size ();
	const int lenStr2 = str2.size ();

	// recopie des chaînes dans des tableaux d'octets
	const QByteArray baCar1 = (str1.toLocal8Bit ()).prepend (' ');
	const QByteArray baCar2 = (str2.toLocal8Bit ()).prepend (' ');

	int i, i_1, i_2;
	int j, j_1, j_2;

	// **************** calcul de la distance ****************

	// d --> ligne et colonne 0
	for (i = 0; i <= lenStr1 ; i++)
	{
		d[i][0] = i;
	}

	for (j = 0; j <= lenStr2 ; j++)
	{
		d[0][j] = j;
	}

	// d --> lignes suivantes
	for (i = 1, i_1 = 0, i_2 = -1 ; i <= lenStr1 ; i++, i_1++, i_2++)
	{
		for (j = 1, j_1 = 0, j_2 = -1 ; j <= lenStr2 ; j++, j_1++, j_2++)
		{
			const int cost = (baCar1.at (i) == baCar2.at (j)) ? 0 : 1;

			int candidate = min3 (d[i_1][j] + 1,		// suppression
								  d[i][j_1] + 1,		// insertion
								  d[i_1][j_1] + cost);	// substitution

			// tester les transpositions
			if (i_1 && j_1 && baCar1.at (i) == baCar2.at (j_1) && baCar1.at (i_1) == baCar2.at (j))
			{
				candidate = min2 (candidate,
								  d[i_2][j_2] + cost);	// transposition
			}

			if (candidate > limit) return -1;

			d[i][j] = candidate;
		}
	}

	return d[lenStr1][lenStr2];
}
//-------------------------------------------
QStringList Ortho::unknownWords(QString text)
{
	QStringList result;
	static const QString& ponctuation ("?./,;:!()");

	text = text.simplified ();

	QStringList words = text.simplified ().split (QRegExp("\\b"), QString::SkipEmptyParts);

	foreach (QString word, words)
	{
		word = word.simplified ();

		foreach (const QChar& car, ponctuation)
			word.remove (car);

		if (! result.contains (word) && isMispelled (word))
		{
			result << word;
		}
	}

	return result;
}

